//
// Created by baigui on 2021/10/3.
//

#define TAG "FFMPEGDemux"

#include "FFMPEGDemux.h"
#include <block.h>
#include "media/NdkMediaCodec.h"
#include <MediaPlayer.h>

FFMPEGDemux::FFMPEGDemux(weak_ptr<MediaPlayer> player) : Demux(player) {

}

FFMPEGDemux::~FFMPEGDemux() {

    if(mLooper)
        mLooper->quit();

    if( mAVFormatContext )
    {
        if( mAVFormatContext->pb )
        {
            av_free( mAVFormatContext->pb->buffer );
            av_free( mAVFormatContext->pb );
        }
        avformat_close_input( &mAVFormatContext);
    }
    
}

static inline void vlc_init_avformat()
{

    av_log_set_level(AV_LOG_DEBUG);
//    avformat_network_init();

    av_register_all();

}


bool FFMPEGDemux::Open(void *path1) {

    int64_t       i_start_time = -1;
    char         *psz_url = (char*)path1;


    fp = fopen(psz_url, "r");
    if (!fp)
    {
        LOGE( "file Err" );
        return false;
    }

    vlc_init_avformat();

    // don't check key frame at the beginning
    check_key_frame = false;

    // Open video file
    //初始化相关的结构体，识别文件的类型
    if(avformat_open_input(&mAVFormatContext, psz_url, NULL, NULL)!=0)
        return -1; // Couldn't open file

    // Retrieve stream information
    //读取一部分音视频数据，获取音视频的信息
    if(avformat_find_stream_info(mAVFormatContext, NULL)<0)
        return -1; // Couldn't find stream information


    av_dump_format(mAVFormatContext,0,psz_url,0);

    unsigned nb_streams = mAVFormatContext->nb_streams;

    if( !nb_streams )
    {
        LOGE("No streams found");
        return false;
    }

    i_tracks = nb_streams;

    LOGE( "file Err--------" );

    for( unsigned i = 0; i < nb_streams; i++ )
    {
        AVStream *s = mAVFormatContext->streams[i];
        const AVCodecParameters *cp = s->codecpar;

        shared_ptr<MediaStream> stream = make_shared<MediaStream>();

        const char *psz_type = "unknown";

        /* Do not use the cover art as a stream */
        if( s->disposition == AV_DISPOSITION_ATTACHED_PIC )
            continue;

        if( cp->extradata_size > 0 )
        {
            const uint8_t *p_extra = cp->extradata;
            unsigned      i_extra  = cp->extradata_size;
            LOGV("extra data : %s----code: %d",p_extra,i_extra);
        }

        switch( cp->codec_type )
        {
            case AVMEDIA_TYPE_AUDIO:
//                stream->bit_rate = cp->bit_rate;
//                stream.audio.i_channels = cp->channels;
//                stream.audio.i_rate = cp->sample_rate;
//                stream.audio.i_bitspersample = cp->bits_per_coded_sample;
//                stream.audio.i_blockalign = cp->block_align;
                stream->type = audio;

                break;

            case AVMEDIA_TYPE_VIDEO:

                char *mime;

                switch (cp->codec_id) {
                    case AV_CODEC_ID_HEVC:
                        mime = "video/hevc";
                        break;
                    case AV_CODEC_ID_H264:

                        //ffmpeg 不处理h264.因为h264的流编码
                        mime = "video/avc";
                        return false;
//                        break;
                    case AV_CODEC_ID_H263: mime = "video/3gpp"; break;
                    case AV_CODEC_ID_MPEG4: mime = "video/mp4v-es"; break;
                    case AV_CODEC_ID_MPEG2VIDEO:
                        mime = "video/mpeg2";
                        break;
                    case AV_CODEC_ID_WMV3: mime = "video/x-ms-wmv"; break;
                    case AV_CODEC_ID_VC1:  mime = "video/wvc1"; break;
                    case AV_CODEC_ID_VP8:  mime = "video/x-vnd.on2.vp8"; break;
                    case AV_CODEC_ID_VP9:  mime = "video/x-vnd.on2.vp9"; break;
                    default:
                        continue;
                }
                stream->mime = mime;
                stream->video.width = cp->width;
                stream->video.height = cp->height;
                stream->type = video;

                AVRational rate;

                selectVideoTrack = i;

                rate = av_guess_frame_rate( mAVFormatContext, s, NULL );

                if( rate.den && rate.num )
                {
                    stream->video.i_frame_rate = rate.num;
                    stream->video.i_frame_rate_base = rate.den;
                }
                stream->state = SELCECT;
                addStreamInfo(stream);
                LOGV("video stream is :%s  id:%d   size:%dx%d",mime,i,cp->width, cp->height);
                break;
        }

    }

    if( mAVFormatContext->start_time != (int64_t)AV_NOPTS_VALUE )
        i_start_time = mAVFormatContext->start_time * 1000000 / AV_TIME_BASE;

//    LOGD("AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT );
//    LOGD("    - format = %s (%s)",mAVInputFormat->name, mAVInputFormat->long_name );
    LOGD("    - start time = %lld", i_start_time );
    LOGD("    - duration = %ld",
             ( mAVFormatContext->duration != (int64_t)AV_NOPTS_VALUE ) ?
             mAVFormatContext->duration * 1000000 / AV_TIME_BASE : -1 );

    return true;
}

bool FFMPEGDemux::start() {
    LOGV("------------ffmepg demux start");
    mLooper = make_shared<Looper>(shared_from_this());
    mLooper->post(DEMUX_REQUEST_BUFFER, this);
    return true;
}

void FFMPEGDemux::handle(int what, void *) {

    if(what == DEMUX_REQUEST_BUFFER) {

        AVPacket    pkt;
        block_t     *p_frame;
        int64_t     i_start_time;

        /* Read a frame */
        int i_av_ret = av_read_frame( mAVFormatContext, &pkt );
        if( i_av_ret )
        {
            /* Avoid EOF if av_read_frame returns AVERROR(EAGAIN) */
            if( i_av_ret == AVERROR(EAGAIN) )
                return;

            LOGE("ffmpeg read err");
            return ;
        }
        if( pkt.stream_index < 0 || (unsigned) pkt.stream_index >= i_tracks )
        {
            av_packet_unref( &pkt );
            return ;
        }

        const AVStream *p_stream = mAVFormatContext->streams[pkt.stream_index];
        if( p_stream->time_base.den <= 0 )
        {
            LOGV("Invalid time base for the stream %d", pkt.stream_index );
            av_packet_unref( &pkt );
            return ;
        }

//        if( (p_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
//            p_sys->check_key_frame ) {
//            p_sys->check_key_frame = !(pkt.flags & AV_PKT_FLAG_KEY);
//            msg_Dbg( p_demux, "tk[%d] key frame %d", pkt.stream_index, !p_sys->check_key_frame );
//            /*
//            if( p_sys->check_key_frame )
//            {
//                av_packet_unref( &pkt );
//                return 1;
//            }
//            */
//        }

//        if( p_stream->codecpar->codec_id == AV_CODEC_ID_SSA )
//        {
//            p_frame = BuildSsaFrame( &pkt, p_sys->i_ssa_order++ );
//            if( !p_frame )
//            {
//                av_packet_unref( &pkt );
//                return ;
//            }
//        }
//        else if( p_stream->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE )
//        {
//            if( ( p_frame = block_Alloc( pkt.size + 3 ) ) == NULL )
//            {
//                av_packet_unref( &pkt );
//                return 0;
//            }
//            p_frame->p_buffer[0] = 0x20;
//            p_frame->p_buffer[1] = 0x00;
//            memcpy( &p_frame->p_buffer[2], pkt.data, pkt.size );
//            p_frame->p_buffer[p_frame->i_buffer - 1] = 0x3f;
//        }
//        else

//        if( pkt.flags & AV_PKT_FLAG_KEY )
//            p_frame->i_flags |= BLOCK_FLAG_TYPE_I;

        /* Used to avoid timestamps overlow */
//        lldiv_t q;
//        if( mAVFormatContext->start_time != (int64_t)AV_NOPTS_VALUE )
//        {
//            q = lldiv( mAVFormatContext->start_time, AV_TIME_BASE);
//            i_start_time = mAVFormatContext->start_time;
//        }
//        else
//            i_start_time = 0;

//        if( pkt.dts == (int64_t)AV_NOPTS_VALUE )
//            p_frame->i_dts = VLC_TS_INVALID;
//        else
//        {
//            q = lldiv( pkt.dts, p_stream->time_base.den );
//            p_frame->i_dts = q.quot * CLOCK_FREQ *
//                             p_stream->time_base.num + q.rem * CLOCK_FREQ *
//                                                       p_stream->time_base.num /
//                                                       p_stream->time_base.den - i_start_time + VLC_TS_0;
//        }
//
//        if( pkt.pts == (int64_t)AV_NOPTS_VALUE )
//            p_frame->i_pts = VLC_TS_INVALID;
//        else
//        {
//            q = lldiv( pkt.pts, p_stream->time_base.den );
//            p_frame->i_pts = q.quot * CLOCK_FREQ *
//                             p_stream->time_base.num + q.rem * CLOCK_FREQ *
//                                                       p_stream->time_base.num /
//                                                       p_stream->time_base.den - i_start_time + VLC_TS_0;
//        }



//        if( pkt.duration > 0 && p_frame->i_length <= 0 )
//            p_frame->i_length = pkt.duration * CLOCK_FREQ *
//                                p_stream->time_base.num /
//                                p_stream->time_base.den;


//        LOGV( "tk[%d] dts=%PRId64",
//             pkt.stream_index, p_frame->i_dts );

//        if( p_frame->i_dts > VLC_TS_INVALID && p_track->p_es != NULL )
//            p_track->i_pcr = p_frame->i_dts;


//        if( p_track->p_es != NULL )
//            es_out_Send( p_demux->out, p_track->p_es, p_frame );
//        else
//            block_Release( p_frame );


        if (pkt.stream_index == selectVideoTrack){
            {
                if( ( p_frame = new block_t( pkt.size ) ) == nullptr )
                {
                    av_packet_unref( &pkt );
                    return;
                }
                memcpy( p_frame->getbuffer(), pkt.data, pkt.size );
                p_frame->setDataSize(pkt.size);
            }
            if(p_frame->getDataSize() > 0) {
//                LOGV("add video stream id: %d",selectVideoTrack);
                mPlayer.lock()->getVideoDecoder()->QueueBuffer(p_frame);
            } else{
                delete p_frame;
            }
        }

        av_packet_unref( &pkt );

        mLooper->post(DEMUX_REQUEST_BUFFER, nullptr,2);
    }
}

void FFMPEGDemux::requestbuffer() {
    Demux::requestbuffer();
}
